use crate::ast::*;
use crate::error::{CompilerError, Result};
use inkwell::values::{BasicValueEnum, PointerValue};
use inkwell::AddressSpace;

use super::super::LLVMBackend;

impl<'ctx> LLVMBackend<'ctx> {
    /// Get struct pointer from a variable (helper for field access and assignment)
    /// Handles both pointer variables and struct value variables
    pub(super) fn get_struct_pointer_from_variable(
        &self,
        var_name: &str,
        alloca_ptr: PointerValue<'ctx>,
    ) -> Result<PointerValue<'ctx>> {
        // Check variable type to determine if it's a pointer or struct value
        if let Some(var_type) = self.variable_type_map.get(var_name) {
            match var_type {
                Type::Reference(_) => {
                    // Variable holds a pointer, need to load it
                    let loaded = self.builder.build_load(
                        self.context.i8_type().ptr_type(AddressSpace::default()),
                        alloca_ptr,
                        &format!("{}_ptr", var_name),
                    )?;
                    match loaded {
                        BasicValueEnum::PointerValue(p) => Ok(p),
                        _ => Err(CompilerError::internal("Expected pointer value")),
                    }
                }
                _ => {
                    // Variable holds a struct value, alloca IS the struct pointer
                    Ok(alloca_ptr)
                }
            }
        } else {
            // No type info, assume it's a pointer (old behavior)
            let loaded = self.builder.build_load(
                self.context.i8_type().ptr_type(AddressSpace::default()),
                alloca_ptr,
                "loaded_ptr",
            )?;
            match loaded {
                BasicValueEnum::PointerValue(p) => Ok(p),
                _ => Ok(alloca_ptr),
            }
        }
    }

    /// Helper function to get struct pointer, automatically dereferencing references
    fn get_struct_pointer_with_auto_deref(
        &mut self,
        var_name: &str,
        var_value: BasicValueEnum<'ctx>,
    ) -> Result<inkwell::values::PointerValue<'ctx>> {
        if var_value.is_pointer_value() {
            let alloca_ptr = var_value.into_pointer_value();

            // Check if this is a reference type that needs auto-dereferencing
            if let Some(var_type) = self.variable_type_map.get(var_name) {
                match var_type {
                    Type::Reference(_) => {
                        // For reference types, we need to load the pointer first, then use it
                        // This is the same as automatic dereferencing
                        let ptr_value = self.builder.build_load(
                            self.context
                                .i8_type()
                                .ptr_type(inkwell::AddressSpace::default()),
                            alloca_ptr,
                            &format!("{}_ptr", var_name),
                        )?;

                        if let BasicValueEnum::PointerValue(ptr) = ptr_value {
                            return Ok(ptr);
                        }
                    }
                    _ => {
                        // For non-reference types, use the existing logic
                        return self.get_struct_pointer_from_variable(var_name, alloca_ptr);
                    }
                }
            }

            // Fallback to existing logic
            self.get_struct_pointer_from_variable(var_name, alloca_ptr)
        } else {
            Err(CompilerError::internal("Variable is not a pointer"))
        }
    }

    /// Generate field access expression
    pub(super) fn generate_field_access_expression(
        &mut self,
        field_access: &FieldAccessExpr,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the struct pointer
        let struct_ptr = if let Expression::Variable(var_name) = &*field_access.object {
            if let Some(var_value) = self.variable_map.get(var_name) {
                // Use the new helper function that handles reference types automatically
                self.get_struct_pointer_with_auto_deref(var_name, *var_value)?
            } else {
                return Err(CompilerError::internal(&format!(
                    "Undefined variable: {}",
                    var_name
                )));
            }
        } else {
            // Generate the object expression for other cases
            let object_value = self.generate_expression(&field_access.object)?;

            // Handle both pointer and non-pointer values
            match object_value {
                BasicValueEnum::PointerValue(ptr) => ptr,
                BasicValueEnum::StructValue(struct_val) => {
                    // For struct values, allocate temp and store
                    let struct_type = struct_val.get_type();
                    let alloca = self.builder.build_alloca(struct_type, "struct_temp")?;
                    let _ = self.builder.build_store(alloca, struct_val);
                    alloca
                }
                _ => {
                    return Err(CompilerError::internal(
                        "Cannot access field on non-struct value",
                    ));
                }
            }
        };

        let struct_decl_opt =
            self.resolve_struct_for_field_access(&field_access.object, &field_access.field);

        // Look up the field index from the struct declaration
        let field_index = if let Some(struct_decl) = struct_decl_opt {
            // Find the field index by name
            struct_decl
                .fields
                .iter()
                .position(|field| field.name == field_access.field)
                .ok_or_else(|| {
                    CompilerError::internal(&format!("Unknown field: {}", field_access.field))
                })?
        } else {
            // Fallback to hardcoded mapping if struct declaration not found
            match field_access.field.as_str() {
                "x" => 0,
                "y" => 1,
                "name" => 0,
                "age" => 1,
                _ => {
                    return Err(CompilerError::internal(&format!(
                        "Unknown field: {}",
                        field_access.field
                    )))
                }
            }
        };

        // Get the correct struct type from the struct declaration
        let struct_type = if let Some(struct_decl) = struct_decl_opt {
            // Generate LLVM types for each field
            let mut field_types = Vec::new();
            for field in &struct_decl.fields {
                let field_llvm_type =
                    self.nrc_type_to_llvm_type(&Some(field.field_type.clone()))?;
                field_types.push(field_llvm_type.into());
            }
            self.context.struct_type(&field_types, false)
        } else {
            // Fallback to hardcoded type if struct declaration not found
            self.context.struct_type(
                &[
                    self.context
                        .i8_type()
                        .ptr_type(inkwell::AddressSpace::default())
                        .into(),
                    self.context.i32_type().into(),
                ],
                false,
            )
        };

        // Generate GEP instruction to get field pointer
        let field_ptr = unsafe {
            self.builder.build_gep(
                struct_type,
                struct_ptr,
                &[
                    self.context.i32_type().const_int(0, false),
                    self.context.i32_type().const_int(field_index as u64, false),
                ],
                &format!("field_{}", field_access.field),
            )?
        };

        // Load the field value
        let field_type = if let Some(struct_decl) = struct_decl_opt {
            // Find the field type by name
            if let Some(field) = struct_decl
                .fields
                .iter()
                .find(|field| field.name == field_access.field)
            {
                self.nrc_type_to_llvm_type(&Some(field.field_type.clone()))?
            } else {
                return Err(CompilerError::internal(&format!(
                    "Unknown field: {}",
                    field_access.field
                )));
            }
        } else {
            // Fallback to hardcoded type mapping
            if field_access.field == "name" {
                self.context
                    .i8_type()
                    .ptr_type(inkwell::AddressSpace::default())
                    .into()
            } else {
                self.context.i32_type().into()
            }
        };

        let field_value = self
            .builder
            .build_load(field_type, field_ptr, &field_access.field)?;
        Ok(field_value)
    }

    /// Generate index access expression (for arrays and slices)
    pub(super) fn generate_index_access_expression(
        &mut self,
        index_access: &IndexAccessExpr,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the type of the object being indexed
        let object_type = if let Expression::Variable(var_name) = &*index_access.object {
            self.variable_type_map.get(var_name).cloned()
        } else {
            None
        };

        // Generate the object (array or slice) expression
        let object_value = self.generate_expression(&index_access.object)?;

        // Generate the index expression
        let index_value = self.generate_expression(&index_access.index)?;

        // Handle Map access first (before integer conversion, as map keys can be strings)
        if let Some(Type::Map(_)) = &object_type {
            if let Expression::Variable(var_name) = &*index_access.object {
                return self.generate_map_index_access(var_name, index_value);
            } else {
                return Err(CompilerError::internal(
                    "Map index access requires a variable",
                ));
            }
        }

        // Get the index as an integer (for slices and arrays)
        let index_int = if index_value.is_int_value() {
            index_value.into_int_value()
        } else {
            return Err(CompilerError::internal("Index must be an integer"));
        };

        // Handle slice access
        if let Some(Type::Vec(vector_type)) = &object_type {
            // Extract the element type from the slice type
            let element_llvm_type =
                self.nrc_type_to_llvm_type(&Some(*vector_type.element_type.clone()))?;

            if object_value.is_struct_value() {
                let slice_struct = object_value.into_struct_value();

                // Extract the pointer field (index 0) from the slice struct
                let ptr_value = self
                    .builder
                    .build_extract_value(slice_struct, 0, "slice_ptr")?
                    .into_pointer_value();

                // Calculate the element address using GEP
                let element_ptr = unsafe {
                    self.builder.build_gep(
                        element_llvm_type,
                        ptr_value,
                        &[index_int],
                        "slice_element_ptr",
                    )?
                };

                // Load and return the element value
                let element_value =
                    self.builder
                        .build_load(element_llvm_type, element_ptr, "slice_element")?;
                Ok(element_value)
            } else {
                Err(CompilerError::internal("Slice value is not a struct"))
            }
        } else {
            // TODO: Handle array access
            Err(CompilerError::internal(
                "Array index access not yet implemented, or object is not a slice/map",
            ))
        }
    }
}
